/*
Copyright (C) 2008, 2009 Henrik Naess

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using MatrixPlugin.Utils;
using ZoneFiveSoftware.Common.Data.Fitness;
using GpsRunningPlugin.Util;

namespace MatrixPlugin
{
    public class DistanceInfo : IComparable<DistanceInfo>
    {
        #region private variables

        private IActivity m_activity;
        private float m_distance;
        private float m_elevation;
        private bool m_includeStoppedTime;
        private DateTime m_startTime;
        private TimeSpan m_stoppedTime;
        private TimeSpan m_timeElapsed;

        #endregion

        public DistanceInfo(IActivity activity, DateTime startTime, TimeSpan timeElapsed, float distance,
                            TimeSpan stoppedTime, bool includeStoppedTime)
        {
            m_activity = activity;
            m_distance = distance;
            m_includeStoppedTime = includeStoppedTime;
            m_startTime = startTime;
            m_stoppedTime = stoppedTime;
            m_timeElapsed = timeElapsed;
        }

        #region Properties

        public IActivity Activity
        {
            get { return m_activity; }
        }

        public string ActivityId
        {
            get { if (Activity == null)
                    { return null; }
                return Activity.ReferenceId;
            }
        }

        public float Distance
        {
            get { return m_distance; }
            //set { _distance = value; }
        }

        /// <summary>
        /// Distance converted to SI or non-SI system
        /// </summary>
        public string DistanceString
        {
            get { return UnitUtil.Distance.ToString(Distance, "u"); }
        }

        /// <summary>
        /// Time elapsed minus stopped time (if that specified in the includeStoppedTime property)
        /// </summary>
        public TimeSpan EffectiveTime
        {
            get
            {
                if (m_includeStoppedTime)
                    return m_timeElapsed;

                return (m_timeElapsed - StoppedTime);
            }
        }

        public float ElevationInMeters
        {
            get { return m_elevation; }
            set { m_elevation = value; }
        }

        /// <summary>
        /// Elevation converted to SI or non-SI system
        /// </summary>
        public string ElevationString
        {
            get { return UnitUtil.Elevation.ToString(ElevationInMeters, "u"); }
        }

        public string EffectiveTimeString
        {
            get { return UnitUtil.Time.ToString(EffectiveTime); }
        }

        public bool HasElevation
        {
            get { return m_elevation != float.MaxValue; }
        }

        /// <summary>
        /// Include stopped time in calculation
        /// </summary>
        public bool IncludeStoppedTime
        {
            get { return m_includeStoppedTime; }
            //set { _includeStoppedTime = value; }
        }

        public double Speed
        {
            get { return Distance / EffectiveTime.TotalSeconds; }
        }

        public string SpeedString
        {
            get { return UnitUtil.Speed.ToString(Distance/EffectiveTime.TotalSeconds, Activity, "u"); }
        }

        public double Pace
        {
            get { return 1/Speed; }
        }

        public string PaceString
        {
            get { return UnitUtil.Pace.ToString(Distance/EffectiveTime.TotalSeconds, Activity, "u"); }
        }

        public DateTime StartTimeLocalTime
        {
            get { return m_startTime.ToLocalTime(); }
        }

        public DateTime StartTimeUTC
        {
            get { return m_startTime; }
            //set { _startTime = value; }
        }

        public TimeSpan StoppedTime
        {
            get { return m_stoppedTime; }
            //set { _stoppedTime = value; }
        }

        public string StoppedTimeAsFormattedString
        {
            get { return string.Format("{0:0}:{1:00}", (int) StoppedTime.TotalMinutes, StoppedTime.Seconds); }
        }

        public TimeSpan TimeElapsed
        {
            get { return m_timeElapsed; }
            //set { _timeElapsed = value; }
        }

        #endregion

        /// <summary>
        /// Distance converted to SI or non-SI system, compared with base activity
        /// </summary>
        public string DistanceStringComparedWithBaseActivity(DistanceInfo distanceInfoForBaseActivity)
        {
            float diffInMeters = distanceInfoForBaseActivity.Distance - Distance;
            return string.Format("{0} ({1}{2})",
                                 UnitUtil.Distance.ToString(Distance, ""),
                                 this.m_activity.Equals(distanceInfoForBaseActivity) ? "*" :
                                 diffInMeters > 0 ? "+" : "",
                                 UnitUtil.Distance.ToString(diffInMeters, "u"));
        }

        /// <summary>
        /// Elevation converted to SI or non-SI system, compared with base activity
        /// </summary>
        public string ElevationStringComparedWithBaseActivity(DistanceInfo distanceInfoForBaseActivity)
        {
            float diffInMeters = distanceInfoForBaseActivity.ElevationInMeters - ElevationInMeters;
            return string.Format("{0} ({1}{2})",
                                 UnitUtil.Elevation.ToString(ElevationInMeters, ""),
                                 this.m_activity.Equals(distanceInfoForBaseActivity) ? "*" :
                                 diffInMeters > 0 ? "+" : "",
                                 UnitUtil.Elevation.ToString(diffInMeters, "u"));
        }

        public string EffectiveTimeComparedWithBaseActivity(DistanceInfo distanceInfoForBaseActivity)
        {
            double diffInSeconds = (distanceInfoForBaseActivity.EffectiveTime - EffectiveTime).TotalSeconds;
            return string.Format("{0} ({1}{2}s.)",
                                 EffectiveTimeString,
                                 this.m_activity.Equals(distanceInfoForBaseActivity) ? "*" :
                                 diffInSeconds > 0 ? "+" : "", 
                                 diffInSeconds);
        }

        public void SetElevation(float otherElevation, float thisElevation)
        {
            if (otherElevation == float.MaxValue || thisElevation == float.MaxValue)
                ElevationInMeters = float.MaxValue;
            else
                ElevationInMeters = otherElevation - thisElevation;
        }

        public string SpeedStringComparedWithBaseActivity(DistanceInfo distanceInfoForBaseActivity)
        {
            double diff = (distanceInfoForBaseActivity.Speed - Speed);
            return string.Format("{0} ({1}{2})",
                                 UnitUtil.Speed.ToString(Distance/EffectiveTime.TotalSeconds, Activity, "u"),
                                 this.m_activity.Equals(distanceInfoForBaseActivity) ? "*" :
                                 diff > 0 ? "+" : "",
                                 UnitUtil.Speed.ToString(diff, Activity, "u"));
        }

        public string PaceStringComparedWithBaseActivity(DistanceInfo distanceInfoForBaseActivity)
        {
            double diff = (distanceInfoForBaseActivity.Pace - Pace);
            return string.Format("{0} ({1}{2})",
                                 UnitUtil.Pace.ToString(Distance/EffectiveTime.TotalSeconds, Activity, "u"),
                                 this.m_activity.Equals(distanceInfoForBaseActivity) ? "*" :
                                 diff > 0 ? "+" : "",
                                 UnitUtil.Pace.ToString(diff, Activity, "u"));
        }

        #region IComparable Members

        public int CompareTo(DistanceInfo other)
        {
            return EffectiveTime.CompareTo(other.EffectiveTime);
        }

        #endregion

        public int CompareTo(DistanceInfo other, DistanceInfoComparer.ComparisonType comparisonMethod)
        {
            switch (comparisonMethod)
            {
                case DistanceInfoComparer.ComparisonType.Distance:
                    return -Distance.CompareTo(other.Distance);
                case DistanceInfoComparer.ComparisonType.Speed:
                    return
                        -(Distance / EffectiveTime.TotalSeconds).CompareTo(other.Distance / other.EffectiveTime.TotalSeconds);
                case DistanceInfoComparer.ComparisonType.Pace:
                    return
                        (Distance / EffectiveTime.TotalSeconds).CompareTo(other.Distance / other.EffectiveTime.TotalSeconds);
                case DistanceInfoComparer.ComparisonType.Elevation:
                    return -Math.Abs(ElevationInMeters).CompareTo(Math.Abs(other.ElevationInMeters));
                case DistanceInfoComparer.ComparisonType.Time:
                default:
                    return EffectiveTime.CompareTo(other.EffectiveTime);
            }
        }
    }
}